package com.aotain.oframework.exception;

import java.io.PrintWriter;
import java.io.Serializable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Jacob.cw.Zhang@gmail.com
 */
public class AotainBaseException extends java.lang.Exception implements Serializable {

	private static final long serialVersionUID = -766613774467896316L;

	public static final String CODE_UNKNOWN = "UKWN";
	public static final String CODE_GENERAL = "GENL";
	public static final String CODE_INIT = "INIT";
	public static final String CODE_RUNTIME = "RUNT";
	public static final String CODE_DESTORY = "DESY";
	public static final String CODE_FATAL = "FATL";
	public static final String NEW_LINE = System.getProperty("line.separator", "\n");
	private String exCode;
	private Throwable nestedException;
	private List<Exception> exStack = new ArrayList<Exception>();
	private String subCode = "";
	private String subCodeDescription = "";

	/** Creates a new instance of PCCWBaseException */
	public AotainBaseException(String code, String message) {
		this(code, message, null);
	}

	public AotainBaseException(String code, String message, Throwable root) {
		super((message == null ? "" : message), root);
		exCode = (code == null ? "" : code);
		if (root instanceof AotainBaseException) {
			append((AotainBaseException) root);
		} else {
			nestedException = root;
		}
		exStack.add(this);
	}

	private List<Exception> getExceptionStackVector() {
		return exStack;
	}

	private void append(AotainBaseException e) {
		if (e != null) {
			exStack.addAll(e.getExceptionStackVector());
		}
	}

	public String getExCode() {
		return exCode;
	}

	public void setExCode(String exCode) {
		this.exCode = exCode;
	}

	public Throwable getNestedException() {
		return nestedException;
	}

	public void setNestedException(Throwable nestedException) {
		this.nestedException = nestedException;
	}

	public String getSubCode() {
		return subCode;
	}

	public void setSubCode(String subCode) {
		this.subCode = subCode;
	}

	public String getSubCodeDescription() {
		return subCodeDescription;
	}

	public void setSubCodeDescription(String subCodeDescription) {
		this.subCodeDescription = subCodeDescription;
	}
	
	public AotainBaseException getRootException() {
		if (exStack.size() > 0) {
			return ((AotainBaseException) exStack.get(0));
		} else {
			return this;
		}
	}

	public boolean isRoot() {
		return (exStack.size() == 1);
	}

	public AotainBaseException[] getExceptionStack() {
		return exStack.toArray(new AotainBaseException[] {});
	}

	public String getFullStackTrace() {
		java.io.StringWriter swr = new java.io.StringWriter();
		printFullStackTrace(swr);
		return swr.toString();
	}

	public void printFullStackTrace() {
		printFullStackTrace(new java.io.OutputStreamWriter(System.err));
	}

	public void printFullStackTrace(java.io.Writer writer) {
		AotainBaseException[] exStack = getExceptionStack();
		try {
			writer.write("AotainBaseException Stack:" + NEW_LINE);
			writer.write("  (top ) ");
			for (int i = exStack.length - 1; i >= 0; i--) {
				if (i >= 1) {
					writer.write(exStack[i].toString(false) + NEW_LINE);
					writer.write("  (with) ");
				} else {
					Throwable e = exStack[i].getNestedException();
					if (e != null) {
						writer.write(exStack[i].toString(false) + NEW_LINE);
						writer.write("  (root) ");
					} else {
						e = exStack[i];
					}
					if (e instanceof SQLException) {
						writer.write(extractSQLException(e) + NEW_LINE);
						writer.write("         ");
					}
					e.printStackTrace(new PrintWriter(writer));
				}
			}
			writer.flush();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public String toString() {
		return toString(true);
	}

	private String toString(boolean isFullMessage) {
		String s = this.getClass().getName() + " (Code=" + getExCode() + "): " + getMessage();
		if (isFullMessage) {
			if (!isRoot()) {
				AotainBaseException root = getRootException();
				s += "; Root Exception: " + root;
			} else if (nestedException != null) {
				if (nestedException instanceof SQLException) {
					s += "; Nested SQLException: " + extractSQLException(nestedException);
				} else {
					s += "; Nested Exception: " + nestedException;
				}
			}
		}
		return s;
	}

	private String extractSQLException(Throwable e) {
		String s = "";
		if (e instanceof SQLException) {
			SQLException sqle = (SQLException) e;
			s += sqle;
			while ((sqle = sqle.getNextException()) != null) {
				s += " -NextException-> " + sqle;
			}
		}
		return s;
	}

}
